home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Tech Arsenal 1
/
Tech Arsenal (Arsenal Computer).ISO
/
tek-01
/
memopk.zip
/
MEMOPACK.C
< prev
next >
Wrap
C/C++ Source or Header
|
1991-03-31
|
10KB
|
332 lines
/* John T. Opincar, Jr. [OpinSoft]
CID 71631,541
03/31/91
Donated to the public domain
This module implements a "safe" and fast memo packing function.
Memopack() is safe because it can tolerate a power loss at any time
without leaving your database/memo file in an inconsistent state. At
worst, you'll lose ONE memo field, and only if the power loss/three-finger
salute occurs within a very short window. Memopack() is also fast--
in tests that I ran, it ran in about 70% of the time COPY TO required.
Another benefit is that it only uses two file handles and does not
require that you have enough disk space to make a temporary copy of the
*.dbf file.
In the worst case scenario, Memopack() will need the size of the
original *.dbt file available on disk. This occurs when you pack
a memo file which is already in optimal condition.
To use Memopack(), simply include memopack.obj in your link file. You
invoke memopack with the following syntax: memopack(sDbfName).
If you are wondering how Memopack() can be "safe," (or you are skeptical
by nature) then read on. Otherwise, you really don't need to know
anymore to use the function. Memopack() is fault tolerant because at
any stage of its execution, the memo field pointers in the database
always point to a valid memo field. The validity of the pointers is
preserved by using the same DBT file to store a packed copy itself.
Right after each memo is copied to the end of the DBT file, the database
pointer is updated. The first four bytes of the DBT file contain the
block number of the next available 512 byte block in the file. This
pointer is also updated after each memo is written to the bottom of
the file, thus the integrity of the DBT file is preserved at all times
during this process.
Once all of the memos have been packed to the bottom of the DBT file,
the packed memos are copied back to the beginning of the file. Since
the memo pointers in the DBF are still pointing to the copies at the
bottom of the file, an interruption at this stage will not cause any
problems. Of course, the DBT would then be larger than before the
pack was started, but you would just run memopack() again (hopefully
without interruption this time) to shrink it down to the minimal size.
Once the packed memos have been copied to the top of the DBT file,
all of the memo pointers in the DBF file are adjusted to point to the
packed copies. The final step is to reset the pointer at the top of the
DBT file and then truncate the DBT file to its new, optimal size.
*/
/* If you need to recompile for some reason, make sure you set it up for the
appropriate version of Clipper.
*/
#define S87 0
#ifdef S87
#include "nandef.h"
#endif
#include "extend.h"
extern int _topen(byte *, int);
extern int _tclose(int);
extern int _tread(int, byte *, int);
extern int _twrite(int, byte *, int);
extern long _tlseek(int, long, int);
/*---------------------------------READNUM----------------------------------*/
/*
PURPOSE: Reads int or long from file
RETURNS: Long
*/
long readnum(handle, bytecount)
int handle, bytecount;
{
byte abyte;
int i;
long numread = 0;
for (i = 0; i < bytecount; i++) {
_tread(handle, &abyte, 1);
numread = numread | (((long)abyte) << (i << 3));
}
return( numread );
}
/*---------------------------------WRITENUM---------------------------------*/
/*
PURPOSE: Writes int or long to file
*/
void writenum(handle, number, bytecount)
int handle;
long number;
int bytecount;
{
int i;
byte abyte;
for (i = 0; i < bytecount; i++) {
abyte = (byte)(number & 0x000000FF);
_twrite(handle, &abyte, 1);
number = (number / 256);
}
return;
}
/*----------------------------------MSTOL-----------------------------------*/
/*
PURPOSE: Converts hokey ASCII representation of memo block pointers in
database memo fields into a long
RETURNS: Long
*/
long mstol(memoptrstr)
byte *memoptrstr;
{
int i;
long memoptr;
for (i = 0, memoptr = 0; i <= 9; i++) {
if ( memoptrstr[i] == 32 ) {
memoptr = memoptr * 10;
} else {
memoptr = (memoptr * 10) + memoptrstr[i] - 48;
}
}
return( memoptr );
}
/*----------------------------------LTOMS-----------------------------------*/
/*
PURPOSE: Converts long into hokey ASCII representation of memo block pointers
in database field
RETURNS: Returns string in memostr
*/
void ltoms(memoptr, memostr)
long memoptr;
byte *memostr;
{
int i;
for (i = 0; i <= 9; i++) memostr[i] = ' ';
for (i = 9; (i >= 0) && (memoptr > 0); i--) {
memostr[i] = (memoptr % 10) + 48;
memoptr = memoptr / 10;
}
}
/*---------------------------------MSTRINC----------------------------------*/
/*
PURPOSE: Increments hokey ASCII representation of memo block pointers used
in database memo fields
RETURNS: Pointer to string
*/
byte *mstrinc(memostr)
byte *memostr;
{
int i = 9, carry = 1;
while ( carry ) {
carry = 0;
memostr[i]++;
if ( memostr[i] > '9' ) {
memostr[i--] = '0';
carry = 1;
}
}
return( memostr );
}
/*---------------------------------MEMOPACK---------------------------------*/
/*
PURPOSE: The real McCoy.
RETURNS: .T. if memo file pack was successful
*/
CLIPPER MEMOPACK()
{
byte dbfname[13], dbtname[13], *s, *d, *t, signature;
byte fieldname[11], fieldtype, memoptrstr[10], *memoblock;
byte nextstr[10], startptrstr[10], fielddec;
int dbfhandle, dbthandle, fieldlen, offset, headersize, recordsize;
int offsets[64], memocount, curmemo, i;
long recordcount, memoptr, recordptr, currecord, firstblock, nextblock;
long blockcount, source, dest;
/* get and fix up file names */
s = _parc(1);
d = dbfname;
t = dbtname;
while ( *s && (*s != '.') ) {
*d++ = *s;
*t++ = *s++;
}
*d++ = '.'; *d++ = 'd'; *d++ = 'b'; *d++ = 'f'; *d = '\0';
*t++ = '.'; *t++ = 'd'; *t++ = 'b'; *t++ = 't'; *t = '\0';
/* open and verify files */
if ( (dbfhandle = _topen(dbfname, 2)) < 0 ) {
_retl(0);
return;
}
_tread(dbfhandle, &signature, 1);
if ( signature != 0x83 ) {
_tclose(dbfhandle);
_retl(0);
return;
}
if ( (dbthandle = _topen(dbtname, 2)) < 0 ) {
_tclose(dbfhandle);
_retl(0);
return;
}
/* read some essential dbf info */
_tlseek(dbfhandle, (long)4, 0);
recordcount = readnum(dbfhandle, 4);
headersize = readnum(dbfhandle, 2);
recordsize = (int)readnum(dbfhandle, 2);
/* allocate a read/write buffer */
memoblock = _exmgrab(512);
/* calculate the offsets to the memo fields from the dbf header */
_tlseek(dbfhandle, (long)32, 0);
_tread(dbfhandle, fieldname, 11);
offset = 1;
memocount = -1;
while ( fieldname[0] != (char)13 ) {
_tread(dbfhandle, &fieldtype, 1);
_tlseek(dbfhandle, (long)4, 1);
_tread(dbfhandle, &fielddec, 1);
fieldlen = fielddec;
_tread(dbfhandle, &fielddec, 1);
if ( (fieldtype == 'C') && fielddec ) {
fieldlen = (fielddec << 8) | fieldlen;
fielddec = 0;
}
if ( fieldtype == 'M' ) {
offsets[++memocount] = offset;
}
offset += fieldlen;
_tlseek(dbfhandle, (long)14, 1);
_tread(dbfhandle, fieldname, 11);
}
/* Pack everything to past the former end of the memo file, adjusting */
/* the pointers in the dbf as we go. Also adjust the pointer in the
/* memo file to the last block as we go. */
_tlseek(dbthandle, (long)0, 0);
firstblock = readnum(dbthandle, 4);
nextblock = firstblock;
ltoms(nextblock, nextstr);
blockcount = 0;
recordptr = headersize;
/* for each record in the dbf */
for (currecord = 0; currecord < recordcount; currecord++) {
/* for each memo field in the record */
for (curmemo = 0; curmemo <= memocount; curmemo++) {
_tlseek(dbfhandle, recordptr + (long)offsets[curmemo], 0);
_tread(dbfhandle, memoptrstr, 10);
memoptr = mstol(memoptrstr) << 9;
if ( memoptr > 0 ) {
for (i = 0, s = nextstr, d = startptrstr; i <= 9; i++) *d++ = *s++;
i = 512;
/* for each 512 byte block allocated to this particular memo */
while ( i >= 512 ) {
blockcount++;
_tlseek(dbthandle, memoptr, 0);
_tread(dbthandle, memoblock, 512);
_tlseek(dbthandle, nextblock << 9, 0);
_twrite(dbthandle, memoblock, 512);
nextblock++;
mstrinc(nextstr);
i = 0;
s = memoblock;
while ( (i < 512) && (*s != 26) ) {
i++;
s++;
}
memoptr += (long)512;
}
/* avoid disaster by cleaning up after each memo field */
_tlseek(dbthandle, (long)0, 0);
writenum(dbthandle, nextblock, 4);
_tlseek(dbfhandle, recordptr + (long)offsets[curmemo], 0);
_twrite(dbfhandle, startptrstr, 10);
}
}
recordptr += recordsize;
}
/* Now that everything is safely packed at the end of the memo file, */
/* we have to move it back to the top. Note that we don't adjust the */
/* memo pointers in the dbf yet */
source = firstblock << 9;
dest = (long)512;
for (i = 1; i <= blockcount; i++) {
_tlseek(dbthandle, source, 0);
_tread(dbthandle, memoblock, 512);
_tlseek(dbthandle, dest, 0);
_twrite(dbthandle, memoblock, 512);
source += (long)512;
dest += (long)512;
}
/* Now we can adjust the memo pointers in the dbf */
recordptr = headersize;
for (currecord = 0; currecord < recordcount; currecord++) {
for (curmemo = 0; curmemo <= memocount; curmemo++) {
_tlseek(dbfhandle, recordptr + (long)offsets[curmemo], 0);
_tread(dbfhandle, memoptrstr, 10);
nextblock = mstol(memoptrstr);
ltoms(nextblock - firstblock + 1, memoptrstr);
_tlseek(dbfhandle, recordptr + (long)offsets[curmemo], 0);
_twrite(dbfhandle, memoptrstr, 10);
}
recordptr += recordsize;
}
/* Finally, we gleefully chop off all of that wasted space! */
_tlseek(dbthandle, (long)0, 0);
writenum(dbthandle, (dest >> 9), 4);
_tlseek(dbthandle, dest + 1, 0);
_twrite(dbthandle, memoptrstr, 0);
_tclose(dbfhandle);
_tclose(dbthandle);
_exmback(memoblock, 512);
_retl(1);
}